
#include "Auxiliar.h"

#define NUM_THREADS 15

typedef struct {
    int nivel;
    int num;
    int vetNivel[NUM_CLIENTES_NIVEL_1 * TAM_VET];
    int *vetRecebido1, *vetRecebido2;

} atributosDaThread;

//ordena os vetores recebidos das threads a partir dos clientes e realiza ordenacao baseada no Mergesort
void copiarOrdenado(void *atributos) {

    int i, a = 0, b = 0, tamVetRecebido;

    tamVetRecebido = (int) pow(2, (((atributosDaThread*) atributos)->nivel - 2)) * TAM_VET;

    for (i = 0; i < 2 * tamVetRecebido && a < tamVetRecebido && b < tamVetRecebido; i++) {

        if (((atributosDaThread*) atributos)->vetRecebido1[a] < ((atributosDaThread*) atributos)->vetRecebido2[b])
            ((atributosDaThread*) atributos)->vetNivel[i] = ((atributosDaThread*) atributos)->vetRecebido1[a++];

        else
            ((atributosDaThread*) atributos)->vetNivel[i] = ((atributosDaThread*) atributos)->vetRecebido2[b++];
    }

    while (a < tamVetRecebido) {

        ((atributosDaThread*) atributos)->vetNivel[i++] = ((atributosDaThread*) atributos)->vetRecebido1[a++];
    }

    while (b < tamVetRecebido) {

        ((atributosDaThread*) atributos)->vetNivel[i++] = ((atributosDaThread*) atributos)->vetRecebido2[b++];
    }

    pthread_exit(NULL);
}

//gera um vetor aleatório e o ordena com qsort()
void gerarVetorOrdenado(void *atributos) {

    int i;

    printf("\n\n >> Nivel %d - Thread %d: Vetor gerado: ", ((atributosDaThread*) atributos)->nivel, ((atributosDaThread*) atributos)->num);

    srand(time(NULL) + getpid());

    for (i = 0; i < TAM_VET; i++) {

        ((atributosDaThread*) atributos)->vetNivel[i] = 1 + rand() % 1000;
        printf("%d ", ((atributosDaThread*) atributos)->vetNivel[i]);
    }

    qsort(((atributosDaThread*) atributos)->vetNivel, TAM_VET, sizeof (int), comparar);

    pthread_exit(NULL);
}

int main(int argc, char** argv) {

    pthread_t tabelaDeThreads[NUM_THREADS];
    atributosDaThread tabelaDeAtributos[NUM_THREADS];
    struct timespec tinic, tfinal;
    int indThread = 0, i, j, k;

    //para capturar o instante de início do processo
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tinic);

    //neste laco cumpre-se o papel correspondente aos clientes do nivel 1
    while (indThread < NUM_CLIENTES_NIVEL_1) {

        tabelaDeAtributos[indThread].nivel = 1;
        tabelaDeAtributos[indThread].num = indThread;

        pthread_create(&tabelaDeThreads[indThread], NULL, (void*) &gerarVetorOrdenado, (void*) &tabelaDeAtributos[indThread]);
        indThread++;
    }

    //para esperar pelo encerramento de todas as threads
    for (i = 0; i < indThread; i++) {

        if (pthread_join(tabelaDeThreads[i], NULL) != 0) {

            printf("\n\n >> Erro ao receber retorno da thread.");
            exit(1);
        }
    }
    
    k = 0;

    //para cada um dos niveis de servidores
    for (j = 1; j < NUM_NIVEIS; j++) {

        //para cada servidor do nivel
        while (indThread < (int) ((2 * pow(2, j) - 1) * NUM_CLIENTES_NIVEL_1 / pow(2, j))) {

            tabelaDeAtributos[indThread].nivel = j + 1;
            tabelaDeAtributos[indThread].num = indThread;

            tabelaDeAtributos[indThread].vetRecebido1 = tabelaDeAtributos[k++].vetNivel;
            tabelaDeAtributos[indThread].vetRecebido2 = tabelaDeAtributos[k++].vetNivel;
            
            //copia e ordena os vetores do nivel anterior
            pthread_create(&tabelaDeThreads[indThread], NULL, (void*) &copiarOrdenado, (void*) &tabelaDeAtributos[indThread]);

            indThread++;
        }

        //faz com que se espere pelo encerramento de todos os servidores do nivel
        for (i = (int) ((2 * pow(2, j - 1) - 1) * NUM_CLIENTES_NIVEL_1 / pow(2, j - 1)); i < indThread; i++) {

            if (pthread_join(tabelaDeThreads[i], NULL) != 0) {

                printf("\n\n >> Erro ao receber retorno da thread.");
                exit(1);
            }
        }
    }
    
    //para capturar o instante de termino do processo
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tfinal);

    printf("\n\n >> Vetor total ordenado: ");

    for (i = 0; i < NUM_CLIENTES_NIVEL_1 * TAM_VET; i++) {

        printf("%d ", tabelaDeAtributos[NUM_THREADS - 1].vetNivel[i]);

    }
    
    printf("\n\n >> Tempo total de execucao: %ld s e %ld ns \n\n", (long int)diferencaEntreInstantes(tinic, tfinal).tv_sec, (long int)diferencaEntreInstantes(tinic, tfinal).tv_nsec);
}